/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2012/07/11
* File: SceneNode.h
* Blog: http://www.cnblogs.com/zengqh/
**/

#ifndef SceneNode_h__
#define SceneNode_h__

#include "../Core/Core.h"
#include "../Core/Declare.h"

class SceneNode
{
public:
	SceneNode(SceneNode* parent, 
		SceneManager* scene_mgr, 
		s32 id = -1,
		s32 type = 0,
		const glm::vec3& position = glm::vec3(0.0f, 0.0f, 0.0f),
		const glm::vec3& rotation = glm::vec3(0.0f, 0.0f, 0.0f),
		const glm::vec3& scale = glm::vec3(1.0f, 1.0f, 1.0f))
		: _relative_translation(position)
		, _relative_rotation(rotation)
		, _relative_scale(scale)
		, _parent(parent)
		, _scene_mgr(scene_mgr)
		, _visible(true)
		, _id(id)
		, _type(type)
	{

	}

	virtual ~SceneNode()
	{

	}

	virtual void render() = 0;

	virtual std::string getName() const
	{
		return _name;
	}

	virtual void setName(const std::string& name)
	{
		_name = name;
	}

	virtual glm::mat4 getRelativeTransformation() const
	{
		glm::mat4 mat;

		glm::mat4 mat_t = glm::translate(glm::mat4(1.0f), _relative_translation);

		glm::mat4 mat_rx = glm::rotate(glm::mat4(1.0f), _relative_rotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
		glm::mat4 mat_ry = glm::rotate(mat_rx, _relative_rotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
		glm::mat4 mat_rz = glm::rotate(mat_ry, _relative_rotation.z, glm::vec3(0.0f, 0.0f, 1.0f));

		glm::mat4 mat_s = glm::scale(glm::mat4(1.0f), _relative_scale);

		mat = mat_t * mat_rz * mat_s;

		return mat;
	}

	virtual bool isVisible() const
	{
		return _visible;
	}

	virtual void setVisible(bool visible)
	{
		_visible = visible;
	}

	virtual s32 getID() const
	{
		return _id;
	}

	virtual void setID(s32 id)
	{
		_id = id;
	}

	virtual bool addChild(SceneNode* child)
	{
		if (child && child != this)
		{
			_child_list.push_back(child);

			return true;
		}

		return false;
	}

	virtual bool removeChild(SceneNode* child)
	{
		ChildList::iterator iter = 
			std::find(_child_list.begin(), _child_list.end(), child);
		if (iter != _child_list.end())
		{
			_child_list.erase(iter);
			safe_delete(child);
			return true;
		}

		return false;
	}

	virtual void removeAll()
	{
		ChildList::iterator iter = _child_list.begin();
		for(; iter != _child_list.end(); ++iter)
		{
			SceneNode* child = *iter;
			safe_delete(child);
		}

		_child_list.clear();
	}

	virtual void remove()
	{
		_parent->removeChild(this);
	}

	virtual SceneNode* getParent() const
	{
		return _parent;
	}

	virtual void setParent(SceneNode* parent)
	{
		_parent = parent;
	}

	virtual const glm::vec3& getTranslation() const
	{
		return _relative_translation;
	}

	virtual const glm::vec3& getRotation() const
	{
		return _relative_rotation;
	}

	virtual const glm::vec3& getScale() const
	{
		return _relative_scale;
	}

	virtual void setTranslation(const glm::vec3& t)
	{
		_relative_translation = t;
	}

	virtual void setRotation(const glm::vec3& r)
	{
		_relative_rotation = r;
	}

	virtual void setScale(const glm::vec3& s)
	{
		_relative_scale = s;
	}

	virtual s32 getType() const
	{
		return _type;
	}

	virtual void setType(s32 type)
	{
		_type = type;
	}

	virtual SceneManager* getSceneManager() const
	{
		return _scene_mgr;
	}

	virtual void setSceneManager(SceneManager* mgr)
	{
		_scene_mgr = mgr;
	}

	virtual const glm::mat4& getAbsoluteTransformation() const
	{
		return _absolute_mat;
	}

	virtual void updateAbsoluteTransformation()
	{
		if (_parent)
		{
			_absolute_mat = _parent->getAbsoluteTransformation() * getRelativeTransformation();
		}
		else
		{
			_absolute_mat = getRelativeTransformation();
		}
	}

	virtual void setRenderEffectService(EffectService* es) {};

protected:
	s32						_id;
	std::string				_name;
	s32						_type;
	glm::vec3				_relative_translation;
	glm::vec3				_relative_rotation;
	glm::vec3				_relative_scale;
	SceneNode*				_parent;
	SceneManager*			_scene_mgr;
	bool					_visible;

	typedef					std::list<SceneNode*> ChildList;
	ChildList				_child_list;

	glm::mat4				_absolute_mat;
};

#endif    /* SceneNode_h__ */